/*
 * player.c
 *
 *  Created on: 15 nov. 2010
 *      Author: Nicolas Aucouturier, Laurent Reveillere
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <SDL.h>
#include <SDL_image.h>

#include "../include/game.h"
#include "../include/misc.h"
#include "../include/map.h"

struct s_player {
	int x, y;
	SDL_Surface * directed_img[4];
	enum e_way current_way;

	int nb_bomb;
	int nb_life;
	int rang;
	int key;
	int map_num;
};

t_player player_init(int bomb_number, int life_number) {
	t_player player = malloc(sizeof(*player));
	if (!player)
		error("Memory error");

	player->directed_img[WEST] = load_image(IMG_PLAYER_LEFT);
	player->directed_img[EAST] = load_image(IMG_PLAYER_RIGHT);
	player->directed_img[NORTH] = load_image(IMG_PLAYER_UP);
	player->directed_img[SOUTH] = load_image(IMG_PLAYER_DOWN);

	player->current_way = SOUTH;
	player->nb_bomb = bomb_number;
	player->nb_life = life_number;
	player->rang = 1;
	player->key = 0;
	player->map_num = 0;

	return player;
}

void player_free(t_player player) {
	assert(player);
	int i;
	for (i = 0; i < 4; i++)
		SDL_FreeSurface(player->directed_img[i]);

	free(player);
}

int player_get_x(t_player player) {
	assert(player != NULL);
	return player->x;
}

int player_get_y(t_player player) {
	assert(player != NULL);
	return player->y;
}

void player_set_current_way(t_player player, enum e_way way) {
	assert(player);
	player->current_way = way;
}
// gives the player the key
void player_has_key (t_player player){
	assert(player);
	player->key = 1;
}

// Indicates whether the player has the key
int player_get_key (t_player player){
	assert(player);
	return player->key;
}

// Returns the number of bombs left for the player
int player_get_nb_bomb(t_player player) {
	assert(player);
	return player->nb_bomb;
}

// Returns the bombs' rang for the player
int player_get_rang(t_player player) {
	assert(player);
	return player->rang;
}

// Increases the reach of bombs
void player_inc_rang(t_player player) {
	assert(player);
	player->rang += 1;
}

// Decreases the reach of bombs
void player_dec_rang(t_player player) {
	assert(player);
	player->rang -= 1;
}

// Increases the number of bombs
void player_inc_nb_bomb(t_player player) {
	assert(player);
	player->nb_bomb += 1;
}

// Decreases the number of bombs
void player_dec_nb_bomb(t_player player) {
	assert(player);
	player->nb_bomb -= 1;
}

// Returns the player's number of lives
int player_get_nb_life(t_player player) {
	assert(player);
	return player->nb_life;
}

// Increases the number of bombs
void player_inc_nb_life(t_player player) {
	assert(player);
	player->nb_life += 1;
}

// Decreases the number of bombs
void player_dec_nb_life(t_player player) {
	assert(player);
	player->nb_life -= 1;
}

// Load the player position from the map
void player_from_map(t_player player, t_map map) {
	int i, j;
	for (i = 0; i < map_get_width(map); i++) {
		for (j = 0; j < map_get_height(map); j++) {
			if (map_get_cell_type(map, i, j) == CELL_PLAYER) {
				player->x = i;
				player->y = j;
			}
		}
	}
}

// Checks whether the player is in the reach of an exploding bomb
int bomb_explods_player(t_player player, t_map map){
	int x = player_get_x(player);
	int y = player_get_y(player);
	int rang = player_get_rang(player);

	int ji = 1;
	while ( ji <= rang){

		if (	(map_is_inside(map,x+ji,y) && map_get_cell_type(map, x+ji, y) == CELL_BOMB )||// in the right
				(map_is_inside(map,x,y+ji) && map_get_cell_type(map, x, y+ji) == CELL_BOMB )||// down
				(map_is_inside(map,x-ji,y) && map_get_cell_type(map, x-ji, y) == CELL_BOMB )||// in the left
				(map_is_inside(map,x,y-ji) && map_get_cell_type(map, x, y-ji) == CELL_BOMB )||// up
				(map_is_inside(map,x,y) && map_get_cell_type(map, x, y) == CELL_BOMB )
		){
			return 1;
		}
		ji++;
	}

	return 0;
}


static int player_move_aux(t_player player, t_map map, int x1, int y1, int x2,
		int y2) {

	if (!map_is_inside(map, x1, y1))
		return 0; // Stops the player from moving when it's outside the map
	int subtype = map_get_cell_subtype(map,x1,y1); // we take the 4 most significant bits of the CELL
	int new_type = (subtype << 4) | CELL_BONUS ; // then we concaternate it with CELL_BONUS

	switch (map_get_cell_type(map, x1, y1)) {
	case CELL_SCENERY:
		return 0; // Stops the player from moving when it's a tree or a stone
		break;

	case CELL_CASE:
		if (
				(map_is_inside(map, x1, y1+1) || player_get_y(player)+1 == map_get_width(map)) &&
				(map_is_inside(map, x1+1, y1) || player_get_x(player)+1 == map_get_width(map)) &&
				(map_is_inside(map, x1, y1-1) || player_get_y(player) == 0) &&
				(map_is_inside(map, x1-1, y1) || player_get_x(player) == 0) &&
				(map_get_cell_type(map,x2,y2)==CELL_EMPTY)
		){
			map_set_cell_type(map,x2,y2, CELL_CASE);
			map_set_cell_type(map,x1,y1, CELL_EMPTY);// the case is pushed by the player when it's possible
			return 1;								// mainly when the next cell is empty
		}
		return 0;
		break;

	case CELL_BOMB:
		return 0;
		break;

	case CELL_BONUS:
		switch (new_type) { // Depending on which bonus is shown, the respective variable is incremented
							// or decremented.
		case CELL_CASE_LIFE +1 :
		if (player_get_nb_life(player)!=9){
			player_inc_nb_life(player);
		}
		break;

		case CELL_CASE_BOMBINC +1 :
		if (player_get_nb_bomb(player)<9){
			player_inc_nb_bomb(player);
		}
		break;

		case CELL_CASE_BOMBDEC +1 :
		if (player_get_nb_bomb(player)>0){
			player_dec_nb_bomb(player);
		}
		break;

		case CELL_CASE_RANGEINC +1 :
		if (player_get_rang(player)<4){
			player_inc_rang(player);
		}
		break;

		case CELL_CASE_RANGEDEC +1 :
		if (player_get_rang(player)>1){
			player_dec_rang(player);
		}
		break;
		}
		return 1;
		break;

		case CELL_KEY:
			player_has_key(player); // Sets the key variable to one
			return 1;
			break;

		case CELL_GOAL:
			return 1;
			break;

		case CELL_MONSTER: // if the player walks on a monster he loses a life
			if (player_get_nb_life(player)>-1){
				player_dec_nb_life(player);}
			return 0;
			break;

		case CELL_PLAYER:
			break;

		case CELL_DOOR:
			switch (player->map_num){
			case 0:
				map = map_get_new(map, player->map_num); // Teleports the player to map 2
				player->map_num = 1;
				break;
			case 1:
				map = map_get_new(map, player->map_num); // Teleports the player to map 3
				player->map_num = 2;
				break;
			}
			return 1;
			break;

			case CELL_CLOSED_DOOR:
				if (player_get_key(player)){ // The key is needed to be teleported
					switch (player->map_num){
					case 0:
						player->key =0; // when the player goes the map 2 he loses the key
						map = map_get_new(map, player->map_num);
						player->map_num = 1;
						break;
					case 1:
						map = map_get_new(map, player->map_num);
						player->map_num = 2;
						break;
					case 2:
						exit(0); // the game stops when the player reaches the last map
						break;
					return 1;
				}
				}
				else return 0;
				break;

		default:
			break;
	}

	// Player has moved
	return 1;
}

int player_move(t_player player, t_map map) {
	int x = player->x;
	int y = player->y;
	int move = 0;

	switch (player->current_way) {
	case NORTH:
		if (player_move_aux(player, map, x, y - 1, x, y - 2) ) {
			player->y--;
			move = 1;
		}
		break;

	case SOUTH:
		if (player_move_aux(player, map, x, y + 1, x, y + 2) ) {
			player->y++;
			move = 1;
		}
		break;

	case WEST:
		if (player_move_aux(player, map, x - 1, y, x - 2, y)) {
			player->x--;
			move = 1;
		}
		break;

	case EAST:
		if (player_move_aux(player, map, x + 1, y, x + 2, y) ) {
			player->x++;
			move = 1;
		}
		break;
	}

	if (move) {
		map_set_cell_type(map, x, y, CELL_EMPTY);
		map_set_cell_type(map, player->x, player->y, CELL_PLAYER);
	}
	return move;
}

// Sets the player in square one or the position 11,0
// Used to move the player away from the bomb after an explosion to square one.
void player_restart (t_player player, t_map map){
	if (player_get_x(player) == 0 && player_get_y(player) == 0){
		player->x=11;
		map_set_cell_type(map, 11, 0, CELL_PLAYER);
	}
	else {
	player->x=0;
	player->y=0;

	map_set_cell_type(map, 0, 0, CELL_PLAYER);
	}
}



void player_display(t_player player, SDL_Surface *screen) {
	assert(player);
	assert(player->directed_img[player->current_way]);
	assert(screen);

	SDL_Rect place;
	place.x = player->x * SIZE_BLOC;
	place.y = player->y * SIZE_BLOC;

	SDL_BlitSurface(player->directed_img[player->current_way], NULL, screen,
			&place);
}

